﻿using System;
using System.Collections.Generic;
using System.Text;
using Npgsql;
using System.Data;
using System.Configuration;

namespace Ent.AccesoDatos
{
    public class PostgreSqlHelper
    {
        /// <summary>
        /// instancia estatica de la misma clase
        /// </summary>
        private static PostgreSqlHelper postgreSqlHelper;
        public NpgsqlTransaction t;
        /// <summary>
        /// especificar el comando a ejecutar en la BD, en este caso se especificara el nombre del procedimiento
        /// </summary>
        NpgsqlCommand comando = new NpgsqlCommand();

        /// <summary>
        /// se la utiliza para la establecer la conexion con la BD
        /// </summary>
        NpgsqlConnection conexion = new NpgsqlConnection();

        /// <summary>
        /// se utiliza para traer los datos de la base de datos
        /// </summary>
        NpgsqlDataAdapter oda = new NpgsqlDataAdapter();

        /// <summary>
        /// se utiliza para almacenar el conjunto de datos extraidos de la base de datos
        /// </summary>
        DataSet dsDatos = new DataSet();

        /// <summary>
        /// variable utilizada para recorrer los parametros
        /// </summary>
        int i;

        #region "Patron Singleton"
        /// <summary>
        /// El Contructor esta esta protegido para que no se instancie directamente
        /// </summary>
        public PostgreSqlHelper() { }

        /// <summary>
        /// Metodo estatico que permitira acceder a la clase sin instanciar la clase
        /// Se compara que la clase no haya sido instanciada 
        /// <param name="cadena">La cadena de conexion para la BD</param>
        /// <returns></returns>
        public static PostgreSqlHelper ObtenerConexion(string cadena)
        {
            if (postgreSqlHelper == null)//para identificar si ya existe una instancia del objeto
            {
                postgreSqlHelper = new PostgreSqlHelper();//se instancia el objeto
                postgreSqlHelper.conexion = new NpgsqlConnection();//se intancia la conexion
                postgreSqlHelper.conexion.ConnectionString = cadena;//se establece la cadena de conexion
            }
            //NpgsqlHelper.conexion = new NpgsqlConnection();
            //NpgsqlHelper.conexion.ConnectionString = cadena;
            return postgreSqlHelper;//se devuelve una instancia de la claese
        }
        #endregion
        public void CerrarTransaccion()
        {
            t.Commit();
        }
        /// <summary>
        /// Este metodo ejecutar un procedimiento almacenado y devuelve un unico valor 
        /// </summary>
        /// <param name="Procemiento">Nombre del procedimiento</param>
        /// <param name="parametros"> Parametros Necesario para el procedimeinto</param>
        /// <param name="opc">0 Identificar que solo se devolvera un unico valor.</param>
        /// <returns></returns>
        public object ExecuteScalar(string strconexion, string Procemiento, List<Parametro> parametros)
        {
            object resp = new object();
            try//para controlar las excepciones
            {
                comando = new NpgsqlCommand();//se crea la instancia del comando
                conexion.ConnectionString = strconexion;
                conexion.Open(); //se abre la conexion
                comando.Connection = conexion; //se agrega la conexion al comando
                comando.CommandText = Procemiento; //se especifica el nombre del procedimiento
                comando.CommandType = CommandType.StoredProcedure;//se establece el tipo de comando, en este caso procedimientoalmacenado
                if (parametros != null)//para identificar si hay parametros
                {
                    var listaParametros = parseParametros(parametros);
                    for (i = 0; i < listaParametros.Count; i++) //recorre los parametros que se envias de la capa superior
                        comando.Parameters.AddWithValue(listaParametros[i].ParameterName, listaParametros[i].Value);//se agrega los parametros al comando
                        //comando.Parameters.Add(listaParametros[i]);//se agrega los parametros al comando
                }
                resp = comando.ExecuteScalar();//se ejecuta el comando y devuelve un unico valor
                conexion.Close();//se cierra la conexion
            }
            catch
            {
                conexion.Close();//se cierra la conexion
                throw; //enviar la excepcion a la capa superior
            }
            return resp;//se retorna el valor
        }
        
        /// <summary>
        /// Este metodo permite ejecutar un ExecuteNonQuery
        /// </summary>
        /// <param name="Procemiento">Nombre del procedimiento</param>
        /// <param name="parametros"> Parametros Necesario para el procedimeinto</param>
        public void ExecuteNonQuery(string Procemiento, List<Parametro> parametros)
        {
            try
            {

                conexion.ConnectionString = ConfigurationManager.AppSettings.Get("ENTConPostgreSql").ToString();
                comando = new NpgsqlCommand();//se crea la instancia del comando
                conexion.Open(); //se abre la conexion
                comando.Connection = conexion; //se agrega la conexion al comando
                comando.CommandText = Procemiento; //se especifica el nombre del procedimiento
                comando.CommandType = CommandType.StoredProcedure;//se establece el tipo de comando, en este caso procedimientoalmacenado
                if (parametros != null)//para identificar si hay parametros
                {
                    var listaParametros = parseParametros(parametros);
                    for (i = 0; i < listaParametros.Count; i++) //recorre los parametros que se envias de la capa superior
                        comando.Parameters.AddWithValue(listaParametros[i].ParameterName, listaParametros[i].Value);//se agrega los parametros al comando
                        //comando.Parameters.Add(listaParametros[i]);//se agrega los parametros al comando
                }
                comando.ExecuteNonQuery();//Envia a ejecutar una sentencia a la base, en este caso envia a ejecutar un procedimiento
                conexion.Close();//se cierra la conexion
            }
            catch
            {
                conexion.Close();//se cierra la conexion
                throw;//enviar la excepcion a la capa superior
            }
        }

        public DataSet EjecutarStoreProcedure(List<Parametro> parametros, string Procemiento)
        {
            dsDatos = new DataSet();//variable para almacenar los resultados
            try
            {
                conexion.ConnectionString = ConfigurationManager.AppSettings.Get("ENTConPostgreSql").ToString();
                comando = new NpgsqlCommand();//se crea la instancia del comando
                if (conexion.State!=ConnectionState.Open)
                {
                    conexion.Open(); //se abre la conexion
                }
                
                comando.Connection = conexion; //se agrega la conexion al comando
                comando.CommandText = Procemiento; //se especifica el nombre del procedimiento
                comando.CommandType = CommandType.StoredProcedure;//se establece el tipo de comando, en este caso procedimientoalmacenado
                {
                    if (parametros != null) //para identificar si hay parametros
                    {
                        var listaParametros = parseParametros(parametros);
                        for (i = 0; i < listaParametros.Count; i++)
                            //recorre los parametros que se envias de la capa superior
                            //comando.Parameters.Add(listaParametros[i]);
                            comando.Parameters.AddWithValue(listaParametros[i].ParameterName, listaParametros[i].Value);
                                //se agrega los parametros al comando
                    }
                }
                oda.SelectCommand = comando;//asocia el comando al dataadapter
                t = conexion.BeginTransaction();
                oda.Fill(dsDatos);//envia a ejecutar a la base y el resultado obtenido lo almacena en dsDatos
                t.Commit();
                conexion.Close();//se cierra la conexion
            }
            catch
            {
                conexion.Close();//se cierra la conexion
                throw;//enviar la excepcion a la capa superior
            }
            return dsDatos;
        }

        /// <summary>
        /// Este metodo permite ejecutar un ExecuteDataReader
        /// </summary>
        /// <param name="Procemiento">Nombre del procedimiento</param>
        /// <param name="parametros"> Parametros Necesario para el procedimeinto</param>
        /// <returns></returns>
        public NpgsqlDataReader ExecuteReader(string Procemiento, List<Parametro> parametros)
        {
            NpgsqlDataReader dsDatosReader;

            try
            {

                conexion.ConnectionString = ConfigurationManager.AppSettings.Get("ENTConPostgreSql").ToString();
                comando = new NpgsqlCommand();//se crea la instancia del comando
                if (conexion.State==ConnectionState.Closed || conexion.State==ConnectionState.Broken)
                {
                    conexion.Open(); //se abre la conexion
                }
                comando.Connection = conexion; //se agrega la conexion al comando
                comando.CommandText = Procemiento; //se especifica el nombre del procedimiento
                comando.CommandType = CommandType.StoredProcedure;//se establece el tipo de comando, en este caso procedimientoalmacenado
                if (parametros != null)//para identificar si hay parametros
                {
                    var listaParametros = parseParametros(parametros);
                    for (i = 0; i < listaParametros.Count; i++) //recorre los parametros que se envias de la capa superior
                        comando.Parameters.AddWithValue(listaParametros[i].ParameterName, listaParametros[i].Value);//se agrega los parametros al comando
                }
                t = conexion.BeginTransaction();
                
                dsDatosReader = comando.ExecuteReader();
                
                //t.Commit();
                //oda.SelectCommand = comando;//asocia el comando al dataadapter
                //oda.Fill(dsDatos);//envia a ejecutar a la base y el resultado obtenido lo almacena en dsDatos
                //conexion.Close();//se cierra la conexion
            }
            catch
            {
                conexion.Close();//se cierra la conexion
                throw;//enviar la excepcion a la capa superior
            }
            return dsDatosReader;
        }

        /// <summary>
        /// Metodo que permite ejecutar una consulta en la base de datos y devolver el valor en un DataSet
        /// </summary>
        /// <param name="Comando"></param>
        /// <returns></returns>
        public DataSet EjecutarStoreProcedure(string Comando)
        {
            dsDatos = new DataSet();//variable para almacenar los resultados
            try
            {
                conexion.ConnectionString = ConfigurationManager.AppSettings.Get("ENTConPostgreSql").ToString();
                comando = new NpgsqlCommand();//se crea la instancia del comando
                conexion.Open(); //se abre la conexion
                comando.Connection = conexion; //se agrega la conexion al comando
                comando.CommandText = Comando; //se especifica el nombre del procedimiento
                oda.SelectCommand = comando;//asocia el comando al dataadapter
                oda.Fill(dsDatos);//envia a ejecutar a la base y el resultado obtenido lo almacena en dsDatos
                conexion.Close();//se cierra la conexion
            }
            catch
            {
                conexion.Close();//se cierra la conexion
                throw;//enviar la excepcion a la capa superior
            }
            return dsDatos;
        }

        /// <summary>
        /// Este metodo permite ejecutar un ExecuteDataReader
        /// </summary>
        /// <param name="Comando">Sentencia a ejecutar en la base de datos</param>
        /// <returns></returns>
        public NpgsqlDataReader ExecuteReader(string Comando)
        {
            NpgsqlDataReader dsDatosReader;

            try
            {
                conexion.ConnectionString = ConfigurationManager.AppSettings.Get("ENTConPostgreSql").ToString();
                comando = new NpgsqlCommand();//se crea la instancia del comando
                conexion.Open(); //se abre la conexion
                comando.Connection = conexion; //se agrega la conexion al comando
                comando.CommandText = Comando; //se especifica el nombre del procedimiento
                dsDatosReader = comando.ExecuteReader(CommandBehavior.CloseConnection);
                conexion.Close();//se cierra la conexion
            }
            catch
            {
                conexion.Close();//se cierra la conexion
                throw;//enviar la excepcion a la capa superior
            }
            return dsDatosReader;
        }

        public void CerrarSessionDB()
        {
            if (conexion.State==ConnectionState.Open)
            {
                conexion.Close();
            }
            conexion.Close();
        }

        /// <summary>
        /// Metodo que permite realizar una transformacion de la lista de parametros a un arreglo de parametros para Npgsql
        /// </summary>
        /// <param name="parametros">Parametros</param>
        /// <returns></returns>
        List<NpgsqlParameter> parseParametros(List<Parametro> parametros)
        {
            var listaParametros = new List<NpgsqlParameter>();
            foreach (var parametro in parametros)
            {
               var param= new NpgsqlParameter(parametro.Nombre, parametro.Valor);
               param.Direction = parametro.Direccion;                               
               listaParametros.Add(param);
            }
            return listaParametros;
        }
    }

}